#!/usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import print_function

import os
import shutil
import sys
import time

import yaml

from mobileApp import start, Mode, BuilderInfo, Base, Platform, AndroidBuilder, iOSBuilder
from utils import prepare_dir, wildone, file_head, file_txt, AssertNotNull, write_txt_file, env, Assert, \
    file_props, run_cmd, log_start, Fail


class UnityBI(BuilderInfo):
    build_cs = '''\
using UnityEditor;
using System.IO;
using System.Collections;
using UnityEngine;
using System.Collections.Generic;

class PerformBuild
{
    static string[] GetBuildScenes()
    {
        List<string> names = new List<string>();

        foreach(EditorBuildSettingsScene e in EditorBuildSettings.scenes)
        {
            if(e==null)
                continue;

            if(e.enabled)
                names.Add(e.path);
        }
        return names.ToArray();
    }

    static string GetBuildPath()
    {
        return "build/iOS";
    }

    [UnityEditor.MenuItem("Project Cos/Resources/Command Line Build Step")]
    static void CommandLineBuild ()
    {

        Debug.Log("Command line build------------------------------------");

        string[] scenes = GetBuildScenes();
        string mode = "%(mode)s".ToLower().Trim();
        if(mode == "all" || mode == "ios"){
            string path = GetBuildPath ();
            if (scenes == null || scenes.Length == 0 || path == null)
                //return;
                Debug.Log (string.Format ("Path: \\"{0}\\"", path));
            for (int i = 0; i < scenes.Length; ++i) {
                Debug.Log (string.Format ("Scene[{0}]: \\"{1}\\"", i, scenes [i]));
            }

            Debug.Log ("Starting Build!");
            BuildPipeline.BuildPlayer (scenes, path, BuildTarget.iOS, BuildOptions.None);
        }
        if(mode == "all" || mode == "android"){
            string path = GetBuildPathAndroid ();
            if (scenes == null || scenes.Length == 0 || path == null)
                //return;
                Debug.Log (string.Format ("Path: \\"{0}\\"", path));
            for (int i = 0; i < scenes.Length; ++i) {
                Debug.Log (string.Format ("Scene[{0}]: \\"{1}\\"", i, scenes [i]));
            }

            Debug.Log ("Starting Build!");
            BuildPipeline.BuildPlayer (scenes, path, BuildTarget.Android, BuildOptions.AcceptExternalModificationsToPlayer);
        }
    }

    static string GetBuildPathAndroid()
    {
        return "build/android";
    }

}
'''

    def __init__(self, title, version, basedir='./'):
        BuilderInfo.__init__(self)
        self.title = title
        self.bundleID = None
        self.version = version
        self.basedir = basedir
        self.valid_mode = []
        self.output = os.path.join(basedir, 'bin')
        self.set('proj.ios', '%s/build/iOS' % basedir)
        self.set('proj.android', '%s/build/android/*' % basedir)
        self.set('project.pbxproj', '%s/build/iOS/Unity-iPhone.xcodeproj/project.pbxproj' % basedir)
        self.set('Info.plist', '%s/build/iOS/Info.plist' % basedir)

        # 默认的目录
        Assert("key目录不存在", os.path.exists(os.path.join(basedir, 'key')))
        for mode in [Mode.DEBUG, Mode.RELEASE, Mode.STORE]:
            try:
                key_path = os.path.join(basedir, 'key', 'ios', mode)
                bundleID = wildone(os.path.join(key_path, '*.bundleID'))
                codeSign = wildone(os.path.join(key_path, '*.codeSign'), fail=False)
                keychains = wildone(os.path.join(key_path, '*.keychain'), fail=False)
                provisioning = wildone(os.path.join(key_path, '*.mobileprovision'), fail=False)
                passwd = wildone(os.path.join(key_path, '*.passwd'), fail=False)
                version_file = wildone(os.path.join(key_path, '*.version'), fail=False)
                if version_file:
                    _ = file_head(version_file, line_num=2)
                    human_version = _[0]
                    build_version = _[1]
                else:
                    human_version = '1.0.'
                    build_version = '1.0.'
                self.set('%s.human_version' % mode, human_version)
                self.set('%s.build_version' % mode, build_version)
                if keychains and provisioning and passwd:
                    codeSign = file_head(codeSign)
                    passwd = file_head(passwd)
                    bundleID = file_head(bundleID)
                    self.sign_info(mode, bundleID, codeSign, provisioning, keychains, passwd)
                else:
                    self.sign_info(mode, bundleID, None, None, None, None)
                plist_template = '%s/key/ios/%s/plist.template' % (basedir, mode)
                if os.path.exists(plist_template):
                    self.set('%s.plist' % mode, file_txt(plist_template))
                else:
                    self.set('%s.plist' % mode, None)
                self.valid_mode.append(mode)
            except:
                pass

    def sign_info(self, mode, bundleID, codeSign, provisioning, keychains, passwd):
        AssertNotNull("没有指定模式", mode)
        self.set('%s.bundleID' % mode, bundleID)
        self.set('%s.codeSign' % mode, codeSign)
        self.set('%s.provisioning' % mode, provisioning)
        self.set('%s.keychains' % mode, keychains)
        self.set('%s.keychains.passwd' % mode, passwd)


class UnityBuilder(Base):
    PLATFORM = {
        Platform.iOS: 'ios',
        Platform.Android: 'android',
        Platform.ALL: 'all',
    }

    def __init__(self, builder, verbose=True, platform=Platform.ALL):
        Base.__init__(self, builder=builder, verbose=verbose)
        self.editor_path = os.path.join(self.basedir, 'Assets', 'Editor')
        self.platform = platform
        self.unity_home = env('UNITY_HOME')
        self.unity_bin = '%s/Contents/MacOS/Unity' % self.unity_home

    def clean_vuforia_mac(self):
        """
        vuforia 修正
        :return:
        """
        vuforia_mac = os.path.join(self.basedir, 'Assets', 'Plugins', 'VuforiaWrapper.bundle')
        if os.path.exists(vuforia_mac):
            shutil.rmtree(vuforia_mac)

    def prepare(self):
        if not os.path.exists(self.editor_path):
            os.makedirs(self.editor_path)
        # 放置脚本
        write_txt_file(os.path.join(self.editor_path, 'BuildProject.cs'),
                       UnityBI.build_cs % {
                           "mode": UnityBuilder.PLATFORM[self.platform],
                       })
        if "更新项目设置":
            editor_settings = os.path.join(self.basedir, 'ProjectSettings', 'EditorSettings.asset')
            with open(editor_settings, mode='rb') as fin:
                Assert("将项目设置为[Force Text]", fin.read(5) == '%YAML')

        with open(os.path.join(self.basedir, 'ProjectSettings', 'ProjectSettings.asset'), mode='r') as psfile:
            # 跳过前3行
            psfile.readline()
            psfile.readline()
            psfile.readline()
            project_settings = yaml.load(psfile)
            content = project_settings['PlayerSettings']

        if "全局设置":
            Assert("请设置[Company Name]", 'companyName' in content)
            Assert("请设置[Product Name]", 'productName' in content)
            Assert("请设置[Company Name]", content['companyName'] != "DefaultCompany")
            if 'applicationIdentifier' in content:
                pass
            elif 'bundleIdentifier' in content:
                Assert("请设置[Bundle Identifier]", content['bundleIdentifier'] != "com.Company.ProductName")
            else:
                Fail("请设置[Bundle Identifier]")

        self.clean_vuforia_mac()

    def build(self):
        logfile = os.path.join(self.basedir, 'out.log')
        if os.path.exists(logfile):
            os.remove(logfile)
        prepare_dir(os.path.join(self.basedir, 'build'))
        run_cmd('%s -projectPath "%s" -quit -batchmode -executeMethod PerformBuild.CommandLineBuild -logFile "%s"' %
                (self.unity_bin, self.basedir, logfile), pwd=self.basedir, log=logfile,
                timeout=3600000)
        if Platform.Android == self.platform or Platform.ALL == self.platform:
            build_props = os.path.join(self.basedir, 'key', 'android', 'build.props')
            if os.path.exists(build_props):
                build_props = file_props(build_props)
                # 更新keystore
                proj_android = wildone(os.path.join(self.basedir, 'build', 'android', "*"))
                with open(os.path.join(proj_android, 'project.properties'), mode='a') as project_properties:
                    project_properties.write(
                        'key.store=%s\n' % wildone(os.path.join(self.basedir, 'key', 'android', '*.keystore')))
                    project_properties.write('key.alias=%s\n' % build_props['key.alias'])
                    project_properties.write('key.store.password=%s\n' % build_props['key.store.password'])
                    project_properties.write('key.alias.password=%s\n' % build_props['key.alias.password'])


def link(src, dst):
    if os.path.exists(dst):
        if os.path.islink(dst):
            os.remove(dst)
        else:
            shutil.rmtree(dst)
    os.symlink(src, dst)


def main():
    log_start(time.time())
    if len(sys.argv) < 4:
        print("""\
Usage:
    UnityBuilder.py <ios|android|all> <path> <title> <version> [all]
""")
        exit(1)
    platform = Platform.parse(sys.argv[1])
    path = os.path.abspath(sys.argv[2])
    title = sys.argv[3]
    version = sys.argv[4]
    if len(sys.argv) >= 6:
        mode = Mode.parse(sys.argv[5])
    else:
        mode = Mode.ALL
    bi = UnityBI(title, version, path)
    prepare_dir(bi.output)
    library = os.path.join(bi.basedir, 'Library')
    library_android = os.path.join(bi.basedir, 'Library_Android')
    library_ios = os.path.join(bi.basedir, 'Library_iOS')

    prepare_dir(library_android, clean=False)
    prepare_dir(library_ios, clean=False)

    temp = os.path.join(bi.basedir, 'Temp')
    temp_android = os.path.join(bi.basedir, 'Temp_Android')
    temp_ios = os.path.join(bi.basedir, 'Temp_iOS')

    prepare_dir(temp_android, clean=False)
    prepare_dir(temp_ios, clean=False)

    # 先iOS(因为部分文件略大)
    if Platform.ALL == platform or Platform.iOS == platform:
        link(library_ios, library)
        link(temp_ios, temp)
        start(UnityBuilder(bi, verbose=True, platform=Platform.iOS), mode, clean_output=False)
        # PostBuild
        script = os.path.join(bi.basedir, 'Assets', 'Editor', 'PostProcessBuildPlayer')
        if os.path.exists(script):
            run_cmd('python "%s" "%s" iPhone' % (script, os.path.join(bi.basedir, 'build', 'iOS')))
        # 默认取消debug版本了
        # start(iOSBuilder(bi, verbose=True), Mode.DEBUG, clean_output=False)
        if mode == Mode.ALL or mode == Mode.ENTERPRISE:
            start(iOSBuilder(bi, verbose=True), Mode.ENTERPRISE, clean_output=False)
        if mode == Mode.ALL or mode == Mode.STORE:
            start(iOSBuilder(bi, verbose=True), Mode.STORE, clean_output=False)
    if Platform.ALL == platform or Platform.Android == platform:
        link(library_android, library)
        link(temp_android, temp)
        start(UnityBuilder(bi, verbose=True, platform=Platform.Android), mode, clean_output=False)
        # PostBuild
        script = os.path.join(bi.basedir, 'Assets', 'Editor', 'PostProcessBuildPlayer')
        if os.path.exists(script):
            android = os.path.join(bi.basedir, 'build', 'Android')
            android = os.path.abspath(os.path.join(wildone('%s/*/src' % android), '..'))
            run_cmd('python "%s" "%s" Android %s' % (script, android, bi.basedir))
        bi.set('proj.android', wildone(bi.get('proj.android')))
        start(AndroidBuilder(bi, verbose=True), Mode.RELEASE, clean_output=False)
    print("END")


if __name__ == '__main__':
    main()
